iT邦幫忙

2023 iThome 鐵人賽

DAY 14
0
Kotlin

讓 Kotlin 程式碼更道地 - 談 Effective Kotlin 與相關的 Design Pattern系列 第 14

D14: 可讀性 - 利用 Kotlin Scope Function 讓你可讀性與安全性的兼顧

  • 分享至 

  • xImage
  •  

本日內容取自 Kotlin Conf 2023 - Kotlin & Functional Programming: pick the best, skip the rest by Urs Peter這個影片,裡面講到如何從指令式程式設計 (Imperative programming) 到利用 scope funtion 達到表達式程式設計最後再到 functional programming,對於 Monad 解釋也很清楚,是個非常好的影片,推薦大家去看

Yes

Scope function 把程式流程化

Imperactive Programming 的程式碼

如果是告訴電腦如何一步一步的執行,我們會稱為指令式程式設計,程式碼是說明如何作 (How to do)。如此的程式碼有比較大的機會使用可變變數,可變集合,較長的變數範圍,在許多地方 return , 這些都違反我們前幾天說的原則,如以下程式碼:

fun devsToFile(fileName: String): Result {
    val client RestClient()
    client.username = "reader" 
    client.secret = System.getenv("pwd")
    client.url="https://..." 
    client.initAccessToken()
    try{
    //檔案處理
        val file File(fileName)
        file.createNewFile()
        file.setWritable(true)
        //取得外部資料
        val devs = client.getAll<Developer>()              
        require (devs.isNotEmpty()) { 
            val msg = "No devs found" 
            LOG.error(msg)
            msg
        }
        //列印到檔案
        devs.forEach(file.appendText(it.toCSV())} 
        return Result (devs. size, file.length())
    }
    finally {
        client.close()
    }
}

以上的例子其實有點故意,是我應該還是會先確定取得外部資料再來開 FileStream.但因為沒有 scope function 比較難限制開發者跳來跳去的思維

Expression Programming 的程式碼

那如果利用Kotlin 的 scope function ,就可限制變數的範圍(scope)並讓流程更易讀,這個就會稱為表達式程式設計,改寫如下例

fun devsToFile(fileName: String): Result =
    //利用 apply 限縮可變空間 
    RestClient().apply{
        username = "reader"
        secret = System.getenv("pwd")
        url = "https://..."
        initAccessToken()
    }.use { client -> //使用 use 在結束自動 close
        client.getAll-Developer>().let { devs ->
            require(devs.isNotEmpty()) {
                "No devs found".also { LOG.error(it) }
            }
            File(fileName).run {
                createNewFile()
                setWritable(true)
                devs.forEach(appendText (it.toCSV())}
                Result(devs.size, length())
            }
        }
    }

我們利用了幾個 Kotlin Scope function

  • apply : 通常用物件的初始化。設定物件的屬性
  • use : 對於 closable 物件取用,並在結尾時關閉
  • let : 直接把值當作參數傳入lambda, 回傳為 lambda 的 return
  • require : 確認物件的 state
  • also : 直接把值當作參數傳入lambda, 回傳為自已

我們再把改造前後的程式放在一起比較,左邊的看起來比較容易閱讀並且安全呢!

https://ithelp.ithome.com.tw/upload/images/20230928/20135701wseP8YuuS9.png

利用高階函式複用控制流程

當我們想改變想的是轉成 CSV 的格式。例如文字要不要加上 ""。我們可以把 (Developer)->String 抽成函數參數,而能接受函數參數的函數叫作 High-order function, 如下我們把
(Developer)->String 變成參數傳入。這個 function 就更有彈性能夠複用

fun devsToFile(fileName: String, toLine:(Developer) -> String ): Result =
    //利用 apply 限縮可變空間 
    RestClient().apply{
        username = "reader"
        secret = System.getenv("pwd")
        url = "https://..."
        initAccessToken()
    }.use { client -> //使用 use 在結束自動 close
        client.getAll-Developer>().let { devs ->
            require(devs.isNotEmpty()) {
                "No devs found".also { LOG.error(it) }
            }
            File(fileName).run {
                createNewFile()
                setWritable(true)
                devs.forEach(appendText (toLine(it))}
                Result(devs.size, length())
            }
        }
    }

每日一推 (G)I-DLE

今天來推這首美式校園風的 Allergy

Yes


上一篇
D13: Kotlin 的 Properties 其實只是披著欄位的函式
下一篇
D15: 利用高階函數達到 - DRY (Don't Repeat Yourself)
系列文
讓 Kotlin 程式碼更道地 - 談 Effective Kotlin 與相關的 Design Pattern30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言